package com.dc.cameraxtest

import android.Manifest
import android.content.ContentValues
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Matrix
import android.graphics.Rect
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.dc.cameraxtest.databinding.ActivityBorderDetectionBinding
import com.dc.cameraxtest.utils.ImageProcessing
import com.dc.cameraxtest.utils.RecognizeTextUtils
import org.opencv.android.Utils
import org.opencv.core.Mat
import org.opencv.core.MatOfPoint
import org.opencv.core.MatOfPoint2f
import org.opencv.core.Point
import org.opencv.core.Scalar
import org.opencv.core.Size
import org.opencv.imgproc.Imgproc
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class BorderDetectionActivity : AppCompatActivity() {

    private lateinit var viewBinding: ActivityBorderDetectionBinding

    private var imageCapture: ImageCapture? = null


    private lateinit var cameraExecutor: ExecutorService

    companion object {
        private const val TAG = "OpenCvActivity"
        private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS =
            mutableListOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO).apply { }
                .toTypedArray()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewBinding = ActivityBorderDetectionBinding.inflate(layoutInflater)
        setContentView(viewBinding.root)

        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this,
                REQUIRED_PERMISSIONS,
                REQUEST_CODE_PERMISSIONS
            )
        }

        // Set up the listeners for take photo and video capture buttons
        viewBinding.btnCapture.setOnClickListener { takePhoto() }
        viewBinding.btnReject.setOnClickListener { showAcceptedRejectedButton(false) }
        viewBinding.btnAccept.setOnClickListener { savePhoto() }
        cameraExecutor = Executors.newSingleThreadExecutor()
    }


    private fun takePhoto() {
        imageCapture?.takePicture(ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageCapturedCallback() {
                override fun onCaptureSuccess(image: ImageProxy) {
                    super.onCaptureSuccess(image)
                    // 获取框的位置和大小
                    val bitmap: Bitmap? = viewBinding.viewFinder.bitmap // 从textureview中把gitmap取出来

                    // 裁剪图像，只获取框内部分
                    if (bitmap != null) {
                        showAcceptedRejectedButton(true) // 这里主要做按键切换，点了拍照按键后，拍照按键隐藏，显示接受和拒绝按键

//                        val dstBitmap = location(bitmap)
//                        viewBinding.ivBitmap.setImageBitmap(dstBitmap) // 不太清楚为什么要这样做

                        val result: Boolean = ImageProcessing.checkIfImageIsBlurred(bitmap)
                        if (!result) {
                            Toast.makeText(applicationContext,"图像过于模糊,请重拍",Toast.LENGTH_SHORT).show()
                        } else {

                        }
                    }
                }

                override fun onError(exception: ImageCaptureException) {
                    super.onError(exception)
                    Log.e(TAG, "onError: ${exception.message}")
                }
            }
        )
    }

    private fun savePhoto() {
        // 存图路径和参数（时间、文件类型）
        val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis())
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, name)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        }
        // 我们希望将输出保存在 MediaStore 中，以便其他应用可以显示它
        val outputOptions = ImageCapture.OutputFileOptions.Builder(
            contentResolver,
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues
        ).build()

        // 拍照后的回调函数
        imageCapture?.takePicture(outputOptions, ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
                }

                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    val msg = "Photo capture succeeded: ${outputFileResults.savedUri}"
                    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                    Log.d(TAG, msg)
                }
            }
        )
    }

    private fun startCamera() {
        // 用于将相机的生命周期绑定到生命周期所有者(MainActivity)。 这消除了打开和关闭相机的任务，因为 CameraX 具有生命周期感知能力。
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        // 向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。我们会在稍后填写它。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。
        cameraProviderFuture.addListener({
            // 将相机的生命周期绑定到应用进程中的 LifecycleOwner。
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
            val preview = Preview.Builder().build()
                .also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } // preview 作为 use case
            imageCapture = ImageCapture.Builder().build()

            val imageAnalyzer = ImageAnalysis.Builder()
                .build()
                .also {
                    it.setAnalyzer(cameraExecutor,
                        LuminosityAnalyzer { luma, bitmap ->
                            Log.d(TAG, "Average luminosity: $luma")
                            runOnUiThread(Runnable {
                                viewBinding.ivBitmap.setImageBitmap(bitmap) // 将计算后的bitmao喂回去
                            })
                        })
                }

            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
            try {
                cameraProvider.unbindAll() // Unbind use cases before rebinding
                cameraProvider.bindToLifecycle(
                    this,
                    cameraSelector,
                    preview,
                    imageCapture,
//                    imageAnalyzer
                ) // Bind use cases to camera
            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc) // 有多种原因可能会导致此代码失败，例如应用不再获得焦点。在此记录日志。
            }
        }, ContextCompat.getMainExecutor(this))
    }


    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
    }


    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(this, "Permissions not granted by the user", Toast.LENGTH_SHORT)
                    .show()
                finish()
            }
        }
    }


    // 捕获按键和接受拒绝按键的切换
    private fun showAcceptedRejectedButton(acceptedRejected: Boolean) {
        if (acceptedRejected) {
//            CameraX.u (preview, imageAnalysis)
            viewBinding.clResult.visibility = View.VISIBLE
            viewBinding.btnCapture.hide()
            viewBinding.viewFinder.visibility = View.GONE
        } else {
            viewBinding.btnCapture.show()
            viewBinding.clResult.visibility = View.GONE
            viewBinding.viewFinder.visibility = View.VISIBLE
            viewBinding.viewFinder.post(Runnable { startCamera() })
        }
    }

    private class LuminosityAnalyzer(private val listener: LumaBitmapListener) :
        ImageAnalysis.Analyzer {
        //COLOR_RGB2GRAY    COLOR_RGB2HSV       COLOR_RGB2Lab
        var currentImageType = Imgproc.COLOR_RGB2HSV // 先确定一开始的模式是RGB2GRAY
        private fun ByteBuffer.toByteArray(): ByteArray {
            rewind()    // Rewind the buffer to zero
            val data = ByteArray(remaining())
            get(data)   // Copy the buffer into a byte array
            return data // Return the byte array
        }

        override fun analyze(image: ImageProxy) {

            val buffer = image.planes[0].buffer
            val data = buffer.toByteArray()
            val pixels = data.map { it.toInt() and 0xFF }
            val luma = pixels.average()

            var bitmap = image.toBitmap()
            val dstBitmap = location(bitmap)
            if (dstBitmap != null) {
                listener(luma, dstBitmap)
            }

            image.close()
        }

        private fun rotateBitmap(bitmap: Bitmap, degrees: Int): Bitmap {
            val matrix = Matrix()
            matrix.postRotate(degrees.toFloat())
            return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
        }

        private fun location(bmp: Bitmap): Bitmap? {
            val originMat = Mat()
            Utils.bitmapToMat(bmp, originMat)
            val resultG = Mat()
            val result = Mat()
            Imgproc.cvtColor(originMat, resultG, Imgproc.COLOR_BGR2GRAY)

            Imgproc.GaussianBlur(resultG, resultG, Size(7.0, 7.0), 0.0)
            Imgproc.Canny(resultG, result, 100.0, 250.0, 3)
            // 膨胀，连接边缘
            Imgproc.dilate(result, result, Mat(), Point(-1.0, -1.0), 4, 1, Scalar(1.0))

            /*val resultBmp = Bitmap.createBitmap(result.cols(), result.rows(), Bitmap.Config.ARGB_8888)
            Utils.matToBitmap(result, resultBmp)*/

            val contours: List<MatOfPoint> = ArrayList()
            val hierarchy = Mat()
            Imgproc.findContours(
                result,
                contours,
                hierarchy,
                Imgproc.RETR_EXTERNAL,
                Imgproc.CHAIN_APPROX_SIMPLE
            )
            //有了轮廓之后，为了方便我们先将轮廓给画出来，这里的resultMat其实就是srcMat，为了区分用了Mat resultMat = srcMat.clone();，
            // 下面代码的意思是，把contours轮廓列表中的所有轮廓(-1表示所有轮廓)在，resultMat上画用黑色(new Scalar(0, 0, 0))粗细为10的线条画出来。
            if (contours.isEmpty()) {
                return null
            }
            val resultMat = resultG.clone()
            var arcLength = 0.0
            var index = 0
            for (i in contours.indices) {
                val source = MatOfPoint2f()
                source.fromList(contours[i].toList())
                if (Imgproc.arcLength(source, true) > arcLength) {
                    arcLength = Imgproc.arcLength(source, true)
                    index = i
                }
            }
            val matOfPoint = contours[index]
            val tempMat = MatOfPoint2f()
            Imgproc.approxPolyDP(
                MatOfPoint2f(*matOfPoint.toArray()),
                tempMat,
                Imgproc.arcLength(MatOfPoint2f(*matOfPoint.toArray()), true) * 0.04,
                true
            )
            val points = tempMat.toArray()
            if (points.size != 4) {
                return null
            }
            val matOfPoints: MutableList<MatOfPoint> = ArrayList()
            matOfPoints.add(MatOfPoint(*tempMat.toArray()))
            Imgproc.drawContours(resultMat, matOfPoints, -1, Scalar(0.0, 0.0, 255.0), 4)
            val resultBmp =
                Bitmap.createBitmap(resultMat.cols(), resultMat.rows(), Bitmap.Config.ARGB_8888)
            Utils.matToBitmap(resultMat, resultBmp)


            return resultBmp
        }



    }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }

}